package rubik;

import aima.core.agent.Action;
import aima.core.search.framework.ActionsFunction;
import aima.core.search.framework.ResultFunction;
import java.util.LinkedHashSet;
import java.util.Set;

public class RubikCubeFunctionFactory {
    private static ActionsFunction _actionsFunction = null;
	private static ResultFunction _resultFunction = null;

	public static ActionsFunction getActionsFunction() {
		if (null == _actionsFunction) {
			_actionsFunction = new RCActionsFunction();
		}
		return _actionsFunction;
	}

	public static ResultFunction getResultFunction() {
		if (null == _resultFunction) {
			_resultFunction = new RCResultFunction();
		}
		return _resultFunction;
	}

	private static class RCActionsFunction implements ActionsFunction {
        @Override
		public Set<Action> actions(Object state) {
			Cube cube = (Cube) state;

			Set<Action> actions = new LinkedHashSet<Action>();

			if (cube.canMove(Cube.U)) {
				actions.add(Cube.U);
                                actions.add(Cube.Ua);
                                actions.add(Cube.U2);
			}
			if (cube.canMove(Cube.D)) {
				actions.add(Cube.D);
                                actions.add(Cube.Da);
                                actions.add(Cube.D2);
			}
			if (cube.canMove(Cube.L)) {
				actions.add(Cube.L);
                                actions.add(Cube.La);
                                actions.add(Cube.L2);
			}
			if (cube.canMove(Cube.R)) {
				actions.add(Cube.R);
                                actions.add(Cube.Ra);
                                actions.add(Cube.R2);
			}
                        if (cube.canMove(Cube.F)) {
				actions.add(Cube.F);
                                actions.add(Cube.Fa);
                                actions.add(Cube.F2);
			}
                        if (cube.canMove(Cube.B)) {
				actions.add(Cube.B);
                                actions.add(Cube.Ba);
                                actions.add(Cube.B2);
			}
			return actions;
		}
	}

	private static class RCResultFunction implements ResultFunction {
		public Object result(Object s, Action a) {
			Cube cube = (Cube) s;                        
			if (Cube.U.equals(a) && cube.canMove(Cube.U)) {
				Cube newCube = new Cube(cube);
				newCube.U();
				return newCube;
			} else if (Cube.Ua.equals(a) && cube.canMove(Cube.Ua)) {
				Cube newCube = new Cube(cube);
				newCube.Ua();
				return newCube;
			} else if (Cube.U2.equals(a) && cube.canMove(Cube.U2)) {
				Cube newCube = new Cube(cube);
				newCube.U2();
				return newCube;
			} else if (Cube.D.equals(a) && cube.canMove(Cube.D)) {
				Cube newCube = new Cube(cube);
				newCube.D();
				return newCube;
			} else if (Cube.Da.equals(a) && cube.canMove(Cube.Da)) {
				Cube newCube = new Cube(cube);
				newCube.Da();
				return newCube;
			} else if (Cube.D2.equals(a) && cube.canMove(Cube.D2)) {
				Cube newCube = new Cube(cube);
				newCube.D2();
				return newCube;
			} else if (Cube.L.equals(a) && cube.canMove(Cube.L)) {
				Cube newCube = new Cube(cube);
				newCube.L();
				return newCube;
			} else if (Cube.La.equals(a) && cube.canMove(Cube.La)) {
				Cube newCube = new Cube(cube);
				newCube.La();
				return newCube;
			} else if (Cube.L2.equals(a) && cube.canMove(Cube.L2)) {
				Cube newCube = new Cube(cube);
				newCube.L2();
				return newCube;
			} else if (Cube.R.equals(a) && cube.canMove(Cube.R)) {
				Cube newCube = new Cube(cube);
				newCube.R();
				return newCube;
			} else if (Cube.Ra.equals(a) && cube.canMove(Cube.Ra)) {
				Cube newCube = new Cube(cube);
				newCube.Ra();
				return newCube;
			} else if (Cube.R2.equals(a) && cube.canMove(Cube.R2)) {
				Cube newCube = new Cube(cube);
				newCube.R2();
				return newCube;
			} else if (Cube.F.equals(a) && cube.canMove(Cube.F)) {
				Cube newCube = new Cube(cube);
				newCube.F();
				return newCube;
			} else if (Cube.Fa.equals(a) && cube.canMove(Cube.Fa)) {
				Cube newCube = new Cube(cube);
				newCube.Fa();
				return newCube;
			} else if (Cube.F2.equals(a) && cube.canMove(Cube.F2)) {
				Cube newCube = new Cube(cube);
				newCube.F2();
				return newCube;
			} else if (Cube.B.equals(a) && cube.canMove(Cube.B)) {
				Cube newCube = new Cube(cube);
				newCube.B();
				return newCube;
			} else if (Cube.Ba.equals(a) && cube.canMove(Cube.Ba)) {
				Cube newCube = new Cube(cube);
				newCube.Ba();
				return newCube;
			} else if (Cube.B2.equals(a) && cube.canMove(Cube.B2)) {
				Cube newCube = new Cube(cube);
				newCube.B2();
				return newCube;
			}			

			// The Action is not understood or is a NoOp
			// the result will be the current state.
			return s;
		}
	}
}
